package com.dinghui.pay.controller.wxpay;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.dinghui.pay.common.ResponseResult;
import com.dinghui.pay.pojo.TbOrder;
import com.dinghui.pay.pojo.TbUser;
import com.dinghui.pay.pojo.param.PayOrderParam;
import com.dinghui.pay.pojo.param.UpdateOrderStatusParam;
import com.dinghui.pay.pojo.param.WxLoginParam;
import com.dinghui.pay.service.OrderService;
import com.dinghui.pay.service.UserService;
import com.dinghui.pay.utils.JwtUtils;
import com.github.wxpay.sdk.MyWxPayConfig;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @version 1.0
 * @author: trt
 * @description: 微信业务接口
 * @create: 2021-05-30 10:10
 */
@CrossOrigin
@RestController
@RequestMapping("/api/wx")
public class WxController {

    @Value("${wxpay.app-id}")
    private String appId;

    @Value("${wxpay.app-secret}")
    private String appSecret;

    @Value("${wxpay.key}")
    private String key;

    @Value("${wxpay.mch-id}")
    private String mchId;

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private OrderService orderService;

    @Autowired
    private MyWxPayConfig myWXPayConfig;


    /**
     * 商户系统向微信平台提交的数据
     * appid 小程序id
     * secret 小程序秘钥
     * js_code 临时登录凭证
     * grant_type 授权码模式 authorization_code
     *
     */
    /**
     * 登录
     * @param param
     * @return
     */
    @PostMapping("/login")
    public ResponseResult login(@RequestBody WxLoginParam param) {
        String url = "https://api.weixin.qq.com/sns/jscode2session"; // 微信平台接口网址
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("js_code", param.getCode());
        map.put("grant_type", "authorization_code");
        String response = HttpUtil.post(url, map);
        JSONObject json = JSONUtil.parseObj(response);
        String openId = json.getStr("openid");
        if (openId == null || openId.length() == 0) {
            return ResponseResult.error("临时登陆凭证错误");
        }
        TbUser user = new TbUser();
        user.setOpenId(openId);
        int count = userService.getCount(user);
        if (count == 0) {
//        TbUser tbUser = userService.getOne(user);
//        if (tbUser == null) {
            user.setNickname(param.getNickname());
            user.setPhoto(param.getPhoto());
            user.setType(2);
            user.setCreateTime(new Date());
            userService.save(user);
        }

        user = new TbUser();
        user.setOpenId(openId);
        user = userService.getOne(user);
        long id = user.getUserId();

//        //用户登录
//        long userId = userService.login(form);
//        //生成token
        String token = jwtUtils.generateToken(id);
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("expire", jwtUtils.getExpire());

        return ResponseResult.success(result);
    }

    /**
     * 生成支付订单的API参数 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     *
     * appid 公众号id
     * mch_id 商户号
     * nonce_str 随机字符串
     * sign 签名
     * body 商品描述
     * out_trade_no 商品订单号
     * total_fee 订单金额
     * spbill_create_ip 终端ip
     * notify_url 通知地址 （商户上线后的地址，现在可以任意地址，只是接收不到通知，不影响支付）
     * trade_type 交易类型
     * openid 微信用户标识
     */
    /**
     * 小程序付款
     * @param param
     * @param header
     * @return
     */
    @PostMapping("/microAppPayOrder")
    public ResponseResult microAppPayOrder(@RequestBody PayOrderParam param, @RequestHeader HashMap header) {
        String token = header.get("token").toString();
        Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = param.getOrderId();

        TbUser user = new TbUser();
        user.setUserId(userId);
        long count = userService.getCount(user);
        //判断用户是否存在
        if (count == 0) {
            return ResponseResult.error("用户不存在");
        }
        //获取用户账号的openid字符串
        String openId = userService.getOne(user).getOpenId();

        //判断用户是否拥有这个订单，并且订单是未支付状态
        TbOrder order = new TbOrder();
        order.setUserId(userId.intValue());
        order.setId(orderId);
        order.setStatus(1);
        count = orderService.getCount(order);
        if (count == 0) {
            return ResponseResult.error("不是有效的订单");
        }

        //验证购物券是否有效
        //验证团购活动是否有效

        //查询订单详情信息
        order = new TbOrder();
        order.setId(orderId);
        order = orderService.getOne(order); //订单对象
        // 向微信平台发出请求，创建支付订单
        String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + ""; //订单金额

        try {
            //向微信平台发出请求，创建支付订单
            WXPay wxPay = new WXPay(myWXPayConfig);
            HashMap map = new HashMap();
            map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
            map.put("body", "订单备注");
            map.put("out_trade_no", order.getCode()); //商品订单流水号
            map.put("total_fee", amount); //订单金额
            map.put("spbill_create_ip", "127.0.0.1"); //客户端IP
            map.put("notify_url", "http://66931e50.nat1.nsloop.com/pay-demo/api/wx/recieveMessage"); //通知回调地址
            map.put("trade_type", "JSAPI");  //调用接口类型
            map.put("openid", openId); //用户授权

            /**
             * 微信平台返回的结果
             * return_code： 状态码
             * app_id: 小程序ID
             * mch_id: 商户ID
             * nonce_str: 随机字符串
             * sign: 数字签名
             * result_code: 业务结果
             * trade_type: 交易类型
             * prepay_id：支付订单ID
             */
            Map<String, String> result = wxPay.unifiedOrder(map); //创建支付订单
            String prepayId = result.get("prepay_id"); //获取支付订单ID
            //System.out.println(prepayId);
            if (prepayId != null) {
                //更新本地商品订单信息，但是不更新商品订单状态
                order.setPrepayId(prepayId); //保存支付订单ID
                order.setPaymentType(1); //支付类型
                orderService.update(order);

                //对返回给小程序的数据生成数字签名
                map.clear();
                map.put("appId", appId);
                String timeStamp = new Date().getTime() + "";
                map.put("timeStamp", timeStamp);
                String nonceStr = WXPayUtil.generateNonceStr();
                map.put("nonceStr", nonceStr);
                map.put("package", "prepay_id=" + prepayId);
                map.put("signType", "MD5");
                String paySign = WXPayUtil.generateSignature(map, key); //生成数字签名

                Map res = new HashMap();
                res.put("package", "prepay_id=" + prepayId);
                res.put("timeStamp", timeStamp);
                res.put("nonceStr", nonceStr);
                res.put("paySign", paySign);
                return ResponseResult.success(res);
            } else {
                return ResponseResult.error("支付订单创建失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error("微信支付模块故障");
        }
    }

    /**
     * 商户系统接收支付结果
     *
     * 微信平台的支付回调
     * （1）微信平台会把用户支付的结果，发送到URL回调地址，格式为XML形式
     * （2）如果商户系统没有响应，微信平台会每隔一段时间再发送一次支付结果。数次之后就不再发送
     * （3）https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8
     *
     * 支付回调的参数
     * return_code：通信状态码
     * result_code: 业务状态码
     * transaction_id: 支付订单ID
     * out_trade_no: 商户订单ID
     * total_fee: 支付金额
     */
    /**
     * 接收消息通知
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping("/recieveMessage")
    public void recieveMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("utf-8");
        Reader reader = request.getReader();
        BufferedReader buffer = new BufferedReader(reader);
        String line = buffer.readLine();
        StringBuffer temp = new StringBuffer();
        while (line != null) {
            temp.append(line);
            line = buffer.readLine();
        }
        buffer.close();
        reader.close();
        String xml=temp.toString();
        if(WXPayUtil.isSignatureValid(xml,key)){
            Map<String, String> map = WXPayUtil.xmlToMap(temp.toString());
            String resultCode = map.get("result_code");
            String returnCode = map.get("return_code");
            if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) {
                String outTradeNo = map.get("out_trade_no");
                TbOrder order = new TbOrder();
                order.setCode(outTradeNo);
                order.setStatus(2);
                orderService.updateOrderStatus(order);
                response.setCharacterEncoding("utf-8");
                response.setContentType("application/xml");
                Writer writer = response.getWriter();
                BufferedWriter bufferedWriter = new BufferedWriter(writer);
                bufferedWriter.write("<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>");
                bufferedWriter.close();
                writer.close();
            }
        }
        else{
            response.sendError(500,"数字签名异常");
        }
    }


    /**
     * 封装主动查询订单支付结果
     * 商户系统不一等会接收到支付通知
     * （1）即便在外网环境下，也不能保证商户系统一定会接收到微信支付结果的通知消息
     * （2）如果微信平台出现故障，有可能用户支付成功以后，没有发送支付通知给商户系统
     * （3）或者说因为网络故障的原因，导致商户系统接收不到支付结果的通知
     *
     * 主动查询支付结果
     * （1）当小程序上面支付成功后，立即向商户系统发出请求，通知商户系统去微信平台查询订单支付的结果。
     */
    /**
     * 更新商品订单状态
     * @param param
     * @param header
     * @return
     */
    @PostMapping("/updateOrderStatus")
    public ResponseResult updateOrderStatus(@RequestBody UpdateOrderStatusParam param,
                               @RequestHeader HashMap header) {

        String token = header.get("token").toString();
        int userId = Integer.parseInt(jwtUtils.getClaimByToken(token).getSubject());
        int orderId = param.getOrderId();
        TbOrder order = new TbOrder();
        order.setUserId(userId);
        order.setId(orderId);
        int count = orderService.getCount(order);
        if (count == 0) {
            return ResponseResult.error("用户与订单不匹配");
        }
        /**
         * 查询支付订单状态必备的参数
         * appid 小程序id
         * mch_id 商户号
         * transaction_id 支付订单ID
         * out_trade_no 商品订单ID
         * nonce_str 随机字符串
         * sign 数字签名
         */
        order = orderService.getOne(order);
        String code = order.getCode();
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("mch_id", mchId);
        map.put("out_trade_no", code);
        map.put("nonce_str", WXPayUtil.generateNonceStr());
        try {
            String sign = WXPayUtil.generateSignature(map, key);
            map.put("sign", sign);
            WXPay wxPay = new WXPay(myWXPayConfig);
            Map<String, String> result = wxPay.orderQuery(map);
            String returnCode = result.get("return_code");
            String resultCode = result.get("result_code");
            if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
                String tradeState = result.get("trade_state");
                if ("SUCCESS".equals(tradeState)) {
                    TbOrder tbOrder = new TbOrder();
                    tbOrder.setCode(code); // 查询条件
                    tbOrder.setStatus(2);
                    tbOrder.setPaymentType(1);
                    orderService.updateOrderStatus(order);
                    return ResponseResult.success("订单状态已修改");
                } else {
                    return ResponseResult.success("订单状态未修改");
                }
            }
            return ResponseResult.success("订单状态未修改");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error("查询支付订单失败");
        }
    }

}